library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

-- According to Z80 UM : The CPU samples the interrupt signal (INT) with 
-- the rising edge of the last clock at the end of any instruction.

entity irq_ctrl is
	generic (
		Vector : natural range 0 to 255 := 0
	);
	port (
		IntSrc 	: in std_logic_vector(7 downto 0);	-- connect interrupt sources to this
		IRQ		: out std_logic; -- active high, connect to CPU interrupt pin
		
		Reset	: in std_logic;--active high
		Clock 	: in std_logic;--rising edge
		ReadActive: in std_logic;--'1' for read, '0' for inactive
		WriteActive: in std_logic;--'1' for write, '0' for inactive
		CS    	: in std_logic;

		Address	: in std_logic_vector(1 downto 0); -- just an arbitrary number, I only need 1 register at the moment
		DataIn	: in std_logic_vector(7 downto 0);
		DataOut	: out std_logic_vector(7 downto 0);
		-- support mode 2 interrupt handling of Z80
		IntAck : in std_logic;
		IntMaskDebug: out std_logic;
		IntSrcDebug: out std_logic
	);
end entity irq_ctrl;


architecture rtl of irq_ctrl is
	signal IntMask : std_logic_vector(7 downto 0) := "00000000";
begin
	generate_int: process(IntSrc, IntMask)
	begin
		if to_integer(unsigned(IntSrc AND IntMask)) > 0 then
			IRQ <= '1';
		else
			IRQ <= '0';
		end if;
	end process generate_int;

	debugOutput: process(IntMask, IntSrc)
	begin
		if to_integer(unsigned(IntMask)) > 0 then
			IntMaskDebug <= '1';
		else
			IntMaskDebug <= '0';
		end if;
		if to_integer(unsigned(IntSrc)) > 0 then
			IntSrcDebug <= '1';
		else
			IntSrcDebug <= '0';
		end if;
	end process debugOutput;
	
	
	control: process(Reset, Clock)
		variable ActiveInts : std_logic_vector(IntSrc'high downto IntSrc'low);
	begin
		if Reset = '0' then
			ActiveInts := IntSrc and IntMask;
			if Clock'event and Clock = '1' then
				if IntAck = '1' and to_integer(unsigned(ActiveInts)) > 0 then
					DataOut <= std_logic_vector(to_unsigned(Vector, DataOut'length)); 
				elsif CS = '1' then
					if ReadActive = '1' then
						-- read current active interrupts
						DataOut <= ActiveInts;
					elsif WriteActive = '1' then
						-- set new mask (enable/disable interrupts, can have side effects!)
						IntMask <= DataIn;
					end if;
				end if;
			end if;
		else
			IntMask <= "00000000";
		end if;
	end process control;
	
end architecture rtl;